feat: Add hardcoded responses for specific questions#307
Conversation
This commit introduces a new conditional block in the `submit` function within `app/actions.tsx` to handle specific user queries. When a user asks 'what is a planet computer?' or 'what is qcx-terra?', the application now provides a predefined, hardcoded response. This change bypasses the standard AI agentic workflow for these specific questions, ensuring a fast and consistent answer. The implementation involves: - Checking the user's input against the two specific questions. - If a match is found, updating the UI and AI state with the predefined answer. - Returning early to prevent further processing by the AI agents.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds an early-branch in app/actions.tsx for two exact queries, streaming a predefined definition, updating AI/UI state, finalizing the stream, disabling further generation, and returning a compact UI state. All other inputs continue through the existing processing path. Minor imports adjusted to support the flow. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant UI as UI Layer
participant H as actions.tsx Handler
participant S as aiState
participant Stream as uiStream
U->>UI: Submit input
UI->>H: onSubmit(input)
alt Input is "what is a planet computer?" or "what is qcx-terra?"
H->>H: Compute definition string
H->>Stream: stream(definition section)
H->>S: append user message
H->>S: append assistant messages (definition, related items, follow-up)
H->>Stream: finalize()
H->>UI: Return compact UI state<br/>(isGenerating=false, component, isCollapsed)
else General input
H->>H: Continue existing processing flow
H->>S: normal state updates
H->>Stream: normal streaming
H->>UI: Return standard UI state
end
note over H,Stream: Early branch bypasses standard processing path
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
|
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
||||||||||||
There was a problem hiding this comment.
- Calling
toLowerCase()onuserInputbefore validating it can cause a runtime crash whenformDatais missing orinputis null. - The
idin the returned object does not match thegroupeIdused for assistant messages, likely breaking message grouping. - Unsafe
formData!usage and JSON-serializingFormDatacan drop non-serializable values and misrepresent user content. - Repeated input normalization and
groupeIdshadowing reduce readability and maintainability.
Summary of changes
- Added an early-return branch in
submitto detect two specific questions by normalizing the input and serving predefined answers. - Updates AI state with a synthetic user message, then appends a streamed bot response UI section.
- Adds three assistant messages (response, related, followup) and completes the UI and generating states before returning.
- Minor formatting change for
userInputinitialization (multiline with semicolon).
| if (userInput.toLowerCase().trim() === 'what is a planet computer?' || userInput.toLowerCase().trim() === 'what is qcx-terra?') { | ||
| const definition = userInput.toLowerCase().trim() === 'what is a planet computer?' | ||
| ? "A planet computer is a proprietary environment aware system that interoperates Climate forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet" | ||
| : "QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land feature predictions from satellite imagery"; | ||
|
|
||
| const content = JSON.stringify(Object.fromEntries(formData!)); | ||
| const type = 'input'; | ||
|
|
There was a problem hiding this comment.
Potential runtime crash: userInput.toLowerCase() is called before verifying userInput is a non-empty string. If formData is absent or input is missing (e.g., null), this will throw. Also, toLowerCase().trim() is duplicated; normalize once and reuse. Consider moving to a safe normalization and using a small lookup table for hardcoded answers to simplify and future-proof this logic.
Suggestion
Consider refactoring this block to safely normalize the input and to use a lookup for hardcoded responses. This also simplifies the ternary and avoids repeated normalization:
const normalizedInput = (typeof userInput === 'string' ? userInput : '').toLowerCase().trim();
const HARD_CODED_ANSWERS: Record<string, string> = {
'what is a planet computer?': 'A planet computer is a proprietary environment aware system that interoperates Climate forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet',
'what is qcx-terra?': 'QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land feature predictions from satellite imagery',
};
if (HARD_CODED_ANSWERS[normalizedInput]) {
const definition = HARD_CODED_ANSWERS[normalizedInput];
const content = JSON.stringify({ input: userInput ?? '' });
const type = 'input';
aiState.update({
...aiState.get(),
messages: [
...aiState.get().messages,
{ id: nanoid(), role: 'user', content, type },
],
});
const definitionStream = createStreamableValue();
definitionStream.done(definition);
uiStream.append(
<Section title="response">
<BotMessage content={definitionStream.value} />
</Section>
);
const groupId = nanoid();
const relatedQueries = { items: [] };
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{ id: groupId, role: 'assistant', content: definition, type: 'response' },
{ id: groupId, role: 'assistant', content: JSON.stringify(relatedQueries), type: 'related' },
{ id: groupId, role: 'assistant', content: 'followup', type: 'followup' },
],
});
isGenerating.done(false);
uiStream.done();
return {
id: groupId,
isGenerating: isGenerating.value,
component: uiStream.value,
isCollapsed: isCollapsed.value,
};
}Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this refactor.
| return { | ||
| id: nanoid(), | ||
| isGenerating: isGenerating.value, | ||
| component: uiStream.value, | ||
| isCollapsed: isCollapsed.value, | ||
| }; |
There was a problem hiding this comment.
The id returned from this early-return block is a fresh nanoid() that does not match the groupeId used for the assistant messages. This likely breaks grouping/association between the UI component and the messages for that interaction.
Suggestion
Return the same group id you used for the assistant messages to keep grouping consistent, e.g.:
return {
id: groupeId, // or `groupId` if you rename it
isGenerating: isGenerating.value,
component: uiStream.value,
isCollapsed: isCollapsed.value,
};Reply with "@CharlieHelps yes please" if you'd like me to add a commit that wires this up consistently.
| const content = JSON.stringify(Object.fromEntries(formData!)); | ||
| const type = 'input'; | ||
|
|
There was a problem hiding this comment.
formData! is unsafe here and JSON.stringify(Object.fromEntries(formData)) can silently drop non-serializable values (e.g., File becomes {}). This can produce misleading content for the stored user message or even throw if formData is undefined. Consider constructing the content safely from known primitives (e.g., the input string).
Suggestion
Prefer stable serialization and avoid the non-null assertion:
const content = JSON.stringify({ input: userInput ?? '' });
const type = 'input';Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this fix.
| id: groupeId, | ||
| role: 'assistant', | ||
| content: definition, | ||
| type: 'response', | ||
| }, | ||
| { | ||
| id: groupeId, | ||
| role: 'assistant', | ||
| content: JSON.stringify(relatedQueries), | ||
| type: 'related', | ||
| }, | ||
| { | ||
| id: groupeId, | ||
| role: 'assistant', | ||
| content: 'followup', | ||
| type: 'followup', | ||
| }, | ||
| ], |
There was a problem hiding this comment.
All three assistant messages use the same id (groupeId). If id is used as a unique identifier (common in React lists/state), this will cause collisions and potentially break rendering or state updates. Use a unique id per message and, if grouping is needed, add a separate groupId property instead of reusing id.
Suggestion
Assign unique IDs to each message:
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: 'assistant',
content: definition,
type: 'response',
},
{
id: nanoid(),
role: 'assistant',
content: JSON.stringify(relatedQueries),
type: 'related',
},
{
id: nanoid(),
role: 'assistant',
content: 'followup',
type: 'followup',
},
],
});
If you prefer grouping semantics, we can add a groupId field while keeping id unique. Reply with "@CharlieHelps yes please" and I’ll prepare the commit.
There was a problem hiding this comment.
Actionable comments posted: 4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
app/actions.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/actions.tsx (2)
components/section.tsx (1)
Section(24-75)components/message.tsx (1)
BotMessage(11-29)
🔇 Additional comments (1)
app/actions.tsx (1)
80-88: Early-branch streaming and graceful finalization look goodCreating a streamable value, emitting the section, finalizing AI/UI state, and early-returning a compact UI state is clean and consistent with the rest of the flow.
Also applies to: 119-128
| const userInput = skip | ||
| ? `{"action": "skip"}` | ||
| : (formData?.get('input') as string); | ||
|
|
||
| if (userInput.toLowerCase().trim() === 'what is a planet computer?' || userInput.toLowerCase().trim() === 'what is qcx-terra?') { | ||
| const definition = userInput.toLowerCase().trim() === 'what is a planet computer?' | ||
| ? "A planet computer is a proprietary environment aware system that interoperates Climate forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet" | ||
| : "QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land feature predictions from satellite imagery"; | ||
|
|
There was a problem hiding this comment.
Null-safe normalization: prevent runtime on file-only or non-text submissions
userInput can be null/undefined (e.g., file-only requests). Calling .toLowerCase() will throw. Compute a normalized string once and guard against skip. Also avoid repeating .toLowerCase().trim().
Apply:
- const userInput = skip
- ? `{"action": "skip"}`
- : (formData?.get('input') as string);
-
- if (userInput.toLowerCase().trim() === 'what is a planet computer?' || userInput.toLowerCase().trim() === 'what is qcx-terra?') {
- const definition = userInput.toLowerCase().trim() === 'what is a planet computer?'
+ const rawInput = skip ? null : formData?.get('input')
+ const userInput = typeof rawInput === 'string' ? rawInput : ''
+ const normalizedInput = userInput.toLowerCase().trim()
+
+ if (!skip && (normalizedInput === 'what is a planet computer?' || normalizedInput === 'what is qcx-terra?')) {
+ const definition = normalizedInput === 'what is a planet computer?'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const userInput = skip | |
| ? `{"action": "skip"}` | |
| : (formData?.get('input') as string); | |
| if (userInput.toLowerCase().trim() === 'what is a planet computer?' || userInput.toLowerCase().trim() === 'what is qcx-terra?') { | |
| const definition = userInput.toLowerCase().trim() === 'what is a planet computer?' | |
| ? "A planet computer is a proprietary environment aware system that interoperates Climate forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet" | |
| : "QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land feature predictions from satellite imagery"; | |
| const rawInput = skip ? null : formData?.get('input') | |
| const userInput = typeof rawInput === 'string' ? rawInput : '' | |
| const normalizedInput = userInput.toLowerCase().trim() | |
| if (!skip && (normalizedInput === 'what is a planet computer?' || normalizedInput === 'what is qcx-terra?')) { | |
| const definition = normalizedInput === 'what is a planet computer?' | |
| ? "A planet computer is a proprietary environment aware system that interoperates Climate forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet" | |
| : "QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land feature predictions from satellite imagery"; |
🤖 Prompt for AI Agents
In app/actions.tsx around lines 55 to 63, the code calls
userInput.toLowerCase().trim() which will throw when userInput is null/undefined
(e.g., file-only submissions); compute a single null-safe normalized string
(e.g., const normalized = (userInput ?? '').toString().toLowerCase().trim())
after handling skip, use that normalized variable in the conditional and
ternary, and ensure you short-circuit when skip is true so you don't attempt
normalization on undefined input.
| const content = JSON.stringify(Object.fromEntries(formData!)); | ||
| const type = 'input'; | ||
|
|
||
| aiState.update({ | ||
| ...aiState.get(), | ||
| messages: [ | ||
| ...aiState.get().messages, | ||
| { | ||
| id: nanoid(), | ||
| role: 'user', | ||
| content, | ||
| type, | ||
| }, | ||
| ], | ||
| }); | ||
|
|
There was a problem hiding this comment.
Do not persist entire FormData; store only the minimal input field
Serializing Object.fromEntries(formData!) risks leaking unrelated fields (and file metadata) into persisted chat messages and can yield {} for File values. Persist only { input } to align with getUIStateFromAIState expectations and minimize data retention.
- const content = JSON.stringify(Object.fromEntries(formData!));
+ // Only persist minimal structured input to avoid leaking other form fields/files
+ const content = JSON.stringify({ input: userInput });
const type = 'input';Committable suggestion skipped: line range outside the PR's diff.
| uiStream.append(answerSection); | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
UI parity: append Follow-up section in the early path
General flow appends a Follow-up section to the stream; the early path should too for consistent UX.
- uiStream.append(answerSection);
+ uiStream.append(answerSection);
+ uiStream.append(
+ <Section title="Follow-up">
+ <FollowupPanel />
+ </Section>
+ );📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| uiStream.append(answerSection); | |
| uiStream.append(answerSection); | |
| uiStream.append( | |
| <Section title="Follow-up"> | |
| <FollowupPanel /> | |
| </Section> | |
| ); |
🤖 Prompt for AI Agents
In app/actions.tsx around lines 89-90, the early return path appends the
answerSection to uiStream but omits appending the Follow-up section, causing UI
inconsistency; after the existing uiStream.append(answerSection) in the early
path, create or obtain the same followUpSection used in the normal flow and call
uiStream.append(followUpSection) there (ensure you reuse the same
structure/props to avoid duplication or mismatch and guard against
null/undefined before appending).
| const groupeId = nanoid(); | ||
| const relatedQueries = { items: [] }; | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Avoid redeclaring groupeId
groupeId is already defined earlier (Line 50). Redeclaration here shadows it unnecessarily.
- const groupeId = nanoid();
- const relatedQueries = { items: [] };
+ const relatedQueries = { items: [] };📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const groupeId = nanoid(); | |
| const relatedQueries = { items: [] }; | |
| const relatedQueries = { items: [] }; |
🤖 Prompt for AI Agents
In app/actions.tsx around lines 91 to 93, the code redeclares "groupeId" which
was already defined at line 50, causing an unnecessary shadow; remove the "const
groupeId = nanoid();" declaration and either reuse the previously declared
variable (assign to it or just reference it) or, if a distinct new id is
required, rename this variable to a different, descriptive identifier (e.g.,
newGroupId) to avoid shadowing and keep naming consistent.
User description
This commit introduces a new conditional block in the
submitfunction withinapp/actions.tsxto handle specific user queries.When a user asks 'what is a planet computer?' or 'what is qcx-terra?', the application now provides a predefined, hardcoded response. This change bypasses the standard AI agentic workflow for these specific questions, ensuring a fast and consistent answer.
The implementation involves:
PR Type
Enhancement
Description
Add hardcoded responses for specific questions
Bypass AI workflow for "planet computer" and "qcx-terra" queries
Implement early return with predefined answers
Update UI and AI state with hardcoded definitions
Diagram Walkthrough
File Walkthrough
actions.tsx
Add hardcoded question handling logicapp/actions.tsx
and "qcx-terra"
Summary by CodeRabbit